package com.ruoyi.pack.service.impl;

import java.util.List;
import java.util.Date;
import java.util.Map;
import java.util.HashMap;
import java.util.ArrayList;
import java.util.stream.Collectors;
import java.math.BigDecimal;
import java.math.RoundingMode;

import lombok.Getter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.AmapUtils;
import com.ruoyi.pack.mapper.PackLocationLogMapper;
import com.ruoyi.pack.mapper.PackLocatorMapper;
import com.ruoyi.pack.mapper.PackContainerMapper;
import com.ruoyi.pack.mapper.PackWarehouseFenceMapper;
import com.ruoyi.pack.domain.PackLocationLog;
import com.ruoyi.pack.domain.PackLocator;
import com.ruoyi.pack.domain.PackContainer;
import com.ruoyi.pack.domain.PackWarehouseFence;
import com.ruoyi.pack.domain.dto.PackContainerWarehouseInfoDTO;
import com.ruoyi.pack.domain.dto.CityTransitAnalysisDto;
import com.ruoyi.pack.service.IPackLocationLogService;
import com.ruoyi.pack.service.IPackContainerService;
import com.ruoyi.pack.service.IPackTransferRecordService;
import com.ruoyi.pack.service.IWayBillService;
import com.ruoyi.pack.domain.WayBill;

/**
 * 定位日志Service业务层处理
 *
 * @author ruoyi
 * @date 2024-01-01
 */
@Service
public class PackLocationLogServiceImpl implements IPackLocationLogService
{
    private static final Logger logger = LoggerFactory.getLogger(PackLocationLogServiceImpl.class);

    @Autowired
    private PackLocationLogMapper packLocationLogMapper;

    @Autowired
    private PackLocatorMapper packLocatorMapper;

    @Autowired
    private PackContainerMapper packContainerMapper;

    @Autowired
    private PackWarehouseFenceMapper packWarehouseFenceMapper;

    @Autowired
    private IPackContainerService packContainerService;

    @Autowired
    private IPackTransferRecordService packTransferRecordService;

    @Autowired
    private AmapUtils amapUtils;

    @Autowired
    private IWayBillService wayBillService;

    /**
     * 查询定位日志
     *
     * @param logId 定位日志主键
     * @return 定位日志
     */
    @Override
    public PackLocationLog selectPackLocationLogByLogId(Long logId)
    {
        return packLocationLogMapper.selectPackLocationLogByLogId(logId);
    }

    /**
     * 查询定位日志列表
     *
     * @param packLocationLog 定位日志
     * @return 定位日志
     */
    @Override
    public List<PackLocationLog> selectPackLocationLogList(PackLocationLog packLocationLog)
    {
        return packLocationLogMapper.selectPackLocationLogList(packLocationLog);
    }

    /**
     * 查询定位日志列表（与运输计划表联表查询）
     *
     * @param packLocationLog 定位日志
     * @return 定位日志集合（包含运输计划偏差信息）
     */
    @Override
    public List<PackLocationLog> selectPackLocationLogListWithTransportPlan(PackLocationLog packLocationLog)
    {
        return packLocationLogMapper.selectPackLocationLogListWithTransportPlan(packLocationLog);
    }

    /**
     * 新增定位日志
     *
     * @param packLocationLog 定位日志
     * @return 结果
     */
    @Override
    @Transactional
    public int insertPackLocationLog(PackLocationLog packLocationLog)
    {
        // 设置上传时间为当前时间（如果未提供）
        if (packLocationLog.getUploadTime() == null)
        {
            packLocationLog.setUploadTime(DateUtils.getNowDate());
        }

        packLocationLog.setCreateTime(DateUtils.getNowDate());

        // 设置创建者信息，处理匿名用户情况
        String createBy = "system";
        try {
            createBy = SecurityUtils.getUsername();
        } catch (Exception e) {
            // 匿名接口调用时可能没有登录用户，使用默认值
            logger.debug("无法获取当前用户信息，使用默认创建者: system");
        }
        packLocationLog.setCreateBy(createBy);

        // 设置删除标志
        packLocationLog.setDelFlag("0");

        // 先插入定位日志
        int result = packLocationLogMapper.insertPackLocationLog(packLocationLog);

        // 在插入定位日志之后，更新定位器和器具的相关信息
        if (result > 0 && packLocationLog.getLocatorId() != null)
        {


            // 1. 更新定位器状态
            updateLocatorStatus(packLocationLog);

            // 2. 更新器具位置信息（如果定位器绑定了器具）
            if (packLocationLog.getContainerId() != null)
            {
                updateContainerLocationInfo(packLocationLog);
            }
        }

        return result;
    }

    /**
     * 修改定位日志
     *
     * @param packLocationLog 定位日志
     * @return 结果
     */
    @Override
    public int updatePackLocationLog(PackLocationLog packLocationLog)
    {
        packLocationLog.setUpdateTime(DateUtils.getNowDate());

        // 设置更新者信息，处理匿名用户情况
        String updateBy = "system";
        try {
            updateBy = SecurityUtils.getUsername();
        } catch (Exception e) {
            // 匿名接口调用时可能没有登录用户，使用默认值
            logger.debug("无法获取当前用户信息，使用默认更新者: system");
        }
        packLocationLog.setUpdateBy(updateBy);
        return packLocationLogMapper.updatePackLocationLog(packLocationLog);
    }

    /**
     * 批量删除定位日志
     *
     * @param logIds 需要删除的定位日志主键
     * @return 结果
     */
    @Override
    public int deletePackLocationLogByLogIds(Long[] logIds)
    {
        return packLocationLogMapper.deletePackLocationLogByLogIds(logIds);
    }

    /**
     * 删除定位日志信息
     *
     * @param logId 定位日志主键
     * @return 结果
     */
    @Override
    public int deletePackLocationLogByLogId(Long logId)
    {
        return packLocationLogMapper.deletePackLocationLogByLogId(logId);
    }

    /**
     * 根据定位器ID查询最新的定位日志
     *
     * @param locatorId 定位器ID
     * @return 定位日志
     */
    @Override
    public PackLocationLog selectLatestLogByLocatorId(Long locatorId)
    {
        return packLocationLogMapper.selectLatestLogByLocatorId(locatorId);
    }

    /**
     * 根据器具ID查询最新的定位日志
     *
     * @param containerId 器具ID
     * @return 定位日志
     */
    @Override
    public PackLocationLog selectLatestLogByContainerId(Long containerId)
    {
        return packLocationLogMapper.selectLatestLogByContainerId(containerId);
    }

    /**
     * 根据运单ID查询最近创建的定位日志
     *
     * @param waybillId 运单ID
     * @return 定位日志
     */
    @Override
    public PackLocationLog selectLatestLogByWaybillId(Long waybillId) {
        return packLocationLogMapper.selectLatestLogByWaybillId(waybillId);
    }

    @Override
    public PackLocationLog selectSecondLatestLogByWaybillId(Long waybillId, Long locatorId) {
        return packLocationLogMapper.selectSecondLatestLogByWaybillId(waybillId, locatorId);
    }

    /**
     * 根据条件查询定位日志列表（高级筛选）
     *
     * @param locatorCode 定位器编码
     * @param containerCode 器具编码
     * @param startTime 开始时间
     * @param endTime 结束时间
     * @param minBatteryLevel 最小电池电量
     * @param maxBatteryLevel 最大电池电量
     * @param minTemperature 最小温度
     * @param maxTemperature 最大温度
     * @param shockLevel 震动等级
     * @param uploadAddress 上传地址
     * @return 定位日志集合
     */
    @Override
    public List<PackLocationLog> selectPackLocationLogByCondition(
        String locatorCode,
        String containerCode,
        String startTime,
        String endTime,
        Integer minBatteryLevel,
        Integer maxBatteryLevel,
        BigDecimal minTemperature,
        BigDecimal maxTemperature,
        Integer shockLevel,
        String uploadAddress
    )
    {
        return packLocationLogMapper.selectPackLocationLogByCondition(
            locatorCode, containerCode, startTime, endTime,
            minBatteryLevel, maxBatteryLevel, minTemperature, maxTemperature,
            shockLevel, uploadAddress);
    }

    /**
     * 上传定位数据（设备接口）
     *
     * @param packLocationLog 定位日志
     * @return 结果
     */
    @Override
    @Transactional
    public int uploadLocationData(PackLocationLog packLocationLog)
    {
        // 1. 验证定位器是否存在
        PackLocator locator = packLocatorMapper.selectPackLocatorByLocatorId(packLocationLog.getLocatorId());
        if (locator == null)
        {
            return 0;
        }

        // 2. 设置上传时间为当前时间
        packLocationLog.setUploadTime(DateUtils.getNowDate());

        // 3. 设置器具ID（如果定位器已绑定器具）
        if (locator.getContainerId() != null)
        {
            packLocationLog.setContainerId(locator.getContainerId());
        }

        // 4. 设置创建者信息
        String username = "system";
        try {
            username = SecurityUtils.getUsername();
        } catch (Exception e) {
            // 设备上传可能没有登录用户，使用默认值
        }
        packLocationLog.setCreateBy(username);
        packLocationLog.setCreateTime(DateUtils.getNowDate());

        // 5. 设置删除标志
        packLocationLog.setDelFlag("0");

        // 6. 保存日志
        int result = packLocationLogMapper.insertPackLocationLog(packLocationLog);

        // 7. 更新定位器状态
        if (result > 0 && packLocationLog.getBatteryLevel() != null)
        {
            packLocatorMapper.updateLocatorStatus(
                packLocationLog.getLocatorId(),
                "1", // 正常状态
                packLocationLog.getBatteryLevel().longValue(),
                null // 不更新信号强度
            );
        }

        return result;
    }

    /**
     * 更新定位器状态
     *
     * @param packLocationLog 定位日志
     */
    private void updateLocatorStatus(PackLocationLog packLocationLog)
    {
        // 设置定位器状态为正常（表示设备在线）
        String locatorStatus = "1";

        // 电池电量
        Long batteryLevel = packLocationLog.getBatteryLevel() != null ?
            packLocationLog.getBatteryLevel().longValue() : null;

        // 信号强度（如果有传感器数据可以从中解析）
        Long signalStrength = null;

        // 更新定位器状态（同时会更新最后在线时间）
        packLocatorMapper.updateLocatorStatus(
            packLocationLog.getLocatorId(),
            locatorStatus,
            batteryLevel,
            signalStrength
        );
    }

    /**
     * 更新器具位置信息
     *
     * @param packLocationLog 定位日志
     */
    private void updateContainerLocationInfo(PackLocationLog packLocationLog)
    {
        // 转换位置信息
        String latitude = packLocationLog.getLatitude() != null ?
            packLocationLog.getLatitude().toString() : null;
        String longitude = packLocationLog.getLongitude() != null ?
            packLocationLog.getLongitude().toString() : null;
        String uploadTime = packLocationLog.getUploadTime() != null ?
            DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD_HH_MM_SS, packLocationLog.getUploadTime()) : null;

        // 调用高德地图API进行逆地址解析
        String province = null;
        String city = null;
        String district = null;
        String detailAddress = packLocationLog.getUploadAddress();

        if (packLocationLog.getLatitude() != null && packLocationLog.getLongitude() != null)
        {
            try {
                java.util.Map<String, String> addressInfo = amapUtils.getAddressFromCoordinate(
                    packLocationLog.getLongitude(),
                    packLocationLog.getLatitude()
                );
                if (addressInfo != null && !addressInfo.isEmpty())
                {
                    province = addressInfo.get("province");
                    city = addressInfo.get("city");
                    district = addressInfo.get("district");

                    // 如果详细地址为空，使用高德返回的地址
                    if (StringUtils.isEmpty(detailAddress))
                    {
                        detailAddress = addressInfo.get("address");
                    }
                }
            } catch (Exception e) {
                // 如果高德API调用失败，记录日志但不影响主流程
                logger.warn("调用高德地图API失败，继续使用原有地址信息: {}", e.getMessage());
            }
        }

        // 更新器具位置信息
        packContainerMapper.updateContainerLocationInfo(
            packLocationLog.getContainerId(),
            latitude,
            longitude,
            uploadTime,
            packLocationLog.getLogId(), // 将在插入后获得
            detailAddress,
            province,
            city,
            district
        );

        // 查询器具可能进入的仓库信息，并判断是否在某个仓库围栏内
        PackContainerWarehouseInfoDTO currentWarehouse = null;
        try {
            // 首先获取器具信息
            PackContainer container = packContainerMapper.selectPackContainerByContainerId(packLocationLog.getContainerId());
            if (container != null) {
                // 获取器具原有的位置状态和仓库信息
                String originalLocationStatus = container.getLocationStatus();
                Long originalWarehouseId = container.getCurrentWarehouseId();

                System.out.println("=== 器具[" + container.getContainerCode() + "]位置状态分析 ===");
                System.out.println("原位置状态: " + ("1".equals(originalLocationStatus) ? "在库" : "在途"));
                System.out.println("原所在仓库ID: " + originalWarehouseId);

                // 查询器具可能进入的仓库信息
                List<PackContainerWarehouseInfoDTO> possibleWarehouses = packContainerMapper.selectContainerPossibleWarehouses(
                    container.getCompanyId(),           // 器具归属公司ID
                    container.getMaterialCode(),        // 器具型号编码
                    container.getCurrentMaterialCode()  // 当前物料编码
                );

                if (possibleWarehouses != null && !possibleWarehouses.isEmpty()) {
                    // 获取当前定位器的经纬度
                    BigDecimal currentLat = packLocationLog.getLatitude();
                    BigDecimal currentLng = packLocationLog.getLongitude();

                    if (currentLat != null && currentLng != null) {
                        // 遍历所有可能的仓库，检查是否在围栏内
                        for (PackContainerWarehouseInfoDTO warehouseInfo : possibleWarehouses) {
                            // 查询仓库电子围栏信息
                            List<PackWarehouseFence> fenceList = packWarehouseFenceMapper.selectPackWarehouseFenceByWarehouseId(warehouseInfo.getWarehouseId());
                            warehouseInfo.setFenceList(fenceList);

                            // 判断当前坐标是否在该仓库的围栏内
                            boolean isInsideFence = isPointInsideWarehouseFence(currentLat, currentLng, fenceList);

                            // 如果在围栏内，记录当前仓库
                            if (isInsideFence && currentWarehouse == null) {
                                currentWarehouse = warehouseInfo;
                            }
                        }
                    }
                }

                // 根据6种情况进行处理
                processContainerLocationChange(container, originalLocationStatus, originalWarehouseId, currentWarehouse, packLocationLog);

            }
        } catch (Exception e) {
            // 查询失败不影响主流程，只记录日志
            logger.warn("查询器具可能进入的仓库信息失败: {}", e.getMessage(), e);
        }
    }

    /**
     * 处理器具位置变化的6种情况
     *
     * @param container 器具信息
     * @param originalLocationStatus 原位置状态
     * @param originalWarehouseId 原仓库ID
     * @param currentWarehouse 当前检测到的仓库
     * @param packLocationLog 定位日志
     */
    private void processContainerLocationChange(PackContainer container, String originalLocationStatus,
                                              Long originalWarehouseId, PackContainerWarehouseInfoDTO currentWarehouse,
                                              PackLocationLog packLocationLog)
    {
        Long currentWarehouseId = currentWarehouse != null ? currentWarehouse.getWarehouseId() : null;
        String currentWarehouseName = currentWarehouse != null ? currentWarehouse.getWarehouseName() : null;

        System.out.println("当前检测到的仓库: " + (currentWarehouseName != null ? currentWarehouseName + "(ID:" + currentWarehouseId + ")" : "无"));

        // 判断处理情况
        if (currentWarehouseId != null && originalWarehouseId != null && currentWarehouseId.equals(originalWarehouseId)) {
            // 新定位库房与原库房相同
            if ("1".equals(originalLocationStatus)) {
                // 情况1：新定位的库房与器具原库房相同，并且器具原来处于在库状态
                System.out.println("=== 情况1：器具在原库房中没有移动，无需操作 ===");
                // 无动作
            } else {
                // 情况2：新定位的库房与器具原库房相同，并且器具原来处于在途状态
                System.out.println("=== 情况2：器具退回了原库房，执行入库操作 ===");
                performContainerInbound(container, currentWarehouse, packLocationLog);
            }
        } else if (currentWarehouseId != null && originalWarehouseId != null && !currentWarehouseId.equals(originalWarehouseId)) {
            // 新定位库房与原库房不同
            if ("1".equals(originalLocationStatus)) {
                // 情况3：新定位的库房与器具原库房不同，并且器具原来处于在库状态
                System.out.println("=== 情况3：器具从原库房直接挪到了新库房，执行出库+入库操作 ===");
                performContainerOutbound(container, originalWarehouseId, packLocationLog);
                performContainerInbound(container, currentWarehouse, packLocationLog);
            } else {
                // 情况4：新定位的库房与器具原库房不同，并且器具原来处于在途状态
                System.out.println("=== 情况4：器具从运输途中到了新库房，执行入库操作 ===");
                performContainerInbound(container, currentWarehouse, packLocationLog);
            }
        } else if (currentWarehouseId == null) {
            // 新定位器具不在任何库房
            if ("1".equals(originalLocationStatus)) {
                // 情况5：新定位器具不在任何库房，并且器具原来处于在库状态
                System.out.println("=== 情况5：器具从原库房到了运输途中，执行出库操作 ===");
                performContainerOutbound(container, originalWarehouseId, packLocationLog);
            } else {
                // 情况6：新定位器具不在任何库房，并且器具原来处于在途状态
                System.out.println("=== 情况6：器具一直处于运输途中，无需操作 ===");
                // 无动作
            }
        }

        System.out.println("=== 位置状态处理完成 ===");
    }

    /**
     * 执行器具出库操作
     *
     * @param container 器具信息
     * @param sourceWarehouseId 来源仓库ID
     * @param packLocationLog 定位日志
     */
    private void performContainerOutbound(PackContainer container, Long sourceWarehouseId, PackLocationLog packLocationLog)
    {
        try {
            System.out.println("--- 执行器具出库操作 ---");

            // 1. 更新器具状态为在途
            packContainerService.updateContainerStatus(container.getContainerId(), "2", null, null);
            System.out.println("已更新器具状态为在途");

            // 2. 获取操作员姓名
            String operatorName = "系统自动";
            try {
                operatorName = SecurityUtils.getUsername();
            } catch (Exception e) {
                // 系统自动触发时可能没有登录用户
            }

            // 3. 创建器具移动记录（出库阶段）
            Long transferRecordId = packTransferRecordService.createOutboundRecord(
                container,
                sourceWarehouseId,
                null, // 来源仓库名称会在Service中查询
                null, // 目标仓库ID为空，表示不确定目标
                null, // 目标仓库名称为空
                packLocationLog.getLogId(),
                operatorName
            );

            if (transferRecordId != null) {
                System.out.println("成功创建移库记录（出库阶段），记录ID: " + transferRecordId);
                System.out.println("  - 器具ID: " + container.getContainerId());
                System.out.println("  - 器具编码: " + container.getContainerCode());
                System.out.println("  - 器具型号: " + container.getMaterialCode() + "/" + container.getMaterialName());
                System.out.println("  - 来源仓库ID: " + sourceWarehouseId);
                System.out.println("  - 移库类型: " + ("2".equals(container.getPackingStatus()) ? "满箱移库" : "空箱移库"));
                System.out.println("  - 移库阶段: 出库（在库→在途）");
                System.out.println("  - 触发定位日志ID: " + packLocationLog.getLogId());
                System.out.println("  - 自动生成: 是");
                System.out.println("  - 操作员: " + operatorName);

                if ("2".equals(container.getPackingStatus())) {
                    // 满箱移库时记录物料信息
                    System.out.println("  - 物料编码: " + container.getCurrentMaterialCode());
                    System.out.println("  - 物料名称: " + container.getCurrentMaterialName());
                    System.out.println("  - 物料数量: " + container.getCurrentMaterialQuantity());
                    System.out.println("  - 内衬方案: " + container.getCurrentLiningScheme());
                }
            } else {
                System.out.println("创建移库记录（出库阶段）失败");
            }

            System.out.println("器具出库操作完成");

        } catch (Exception e) {
            logger.error("器具出库操作失败: {}", e.getMessage(), e);
            System.out.println("器具出库操作失败: " + e.getMessage());
        }
    }

    /**
     * 执行器具入库操作
     *
     * @param container 器具信息
     * @param targetWarehouse 目标仓库信息
     * @param packLocationLog 定位日志
     */
    private void performContainerInbound(PackContainer container, PackContainerWarehouseInfoDTO targetWarehouse, PackLocationLog packLocationLog)
    {
        try {
            System.out.println("--- 执行器具入库操作 ---");

            // 1. 更新器具状态为在库，更新所在仓库和项目
            packContainerService.updateContainerStatus(container.getContainerId(), "1", null, null);

            // 2. 更新器具的仓库和项目信息
            PackContainer updateContainer = new PackContainer();
            updateContainer.setContainerId(container.getContainerId());
            updateContainer.setCurrentWarehouseId(targetWarehouse.getWarehouseId());
            updateContainer.setCurrentProjectId(targetWarehouse.getProjectId());
            updateContainer.setCurrentProjectCode(targetWarehouse.getProjectCode());
            updateContainer.setCurrentProjectName(targetWarehouse.getProjectName());
            updateContainer.setUpdateTime(DateUtils.getNowDate());

            String operatorName = "系统自动";
            try {
                operatorName = SecurityUtils.getUsername();
                updateContainer.setUpdateBy(operatorName);
            } catch (Exception e) {
                updateContainer.setUpdateBy("系统自动");
            }

            packContainerMapper.updatePackContainer(updateContainer);
            System.out.println("已更新器具状态为在库，仓库ID: " + targetWarehouse.getWarehouseId() + "，项目: " + targetWarehouse.getProjectName());

            // 3. 更新移库记录（入库阶段）- 完成出库阶段创建的记录
            boolean updateSuccess = packTransferRecordService.updateToInboundRecord(
                container.getContainerId(),
                targetWarehouse.getWarehouseId(),
                targetWarehouse.getWarehouseName(),
                targetWarehouse.getProjectId(),
                targetWarehouse.getProjectCode(),
                targetWarehouse.getProjectName(),
                packLocationLog.getLogId(),
                operatorName
            );

            if (updateSuccess) {
                System.out.println("成功更新移库记录（入库阶段完成）");
                System.out.println("  - 器具ID: " + container.getContainerId());
                System.out.println("  - 器具编码: " + container.getContainerCode());
                System.out.println("  - 器具型号: " + container.getMaterialCode() + "/" + container.getMaterialName());
                System.out.println("  - 目标仓库: " + targetWarehouse.getWarehouseName() + "(ID:" + targetWarehouse.getWarehouseId() + ")");
                System.out.println("  - 目标项目: " + targetWarehouse.getProjectName() + "(" + targetWarehouse.getProjectCode() + ")");
                System.out.println("  - 移库类型: " + ("2".equals(container.getPackingStatus()) ? "满箱移库" : "空箱移库"));
                System.out.println("  - 移库阶段: 出库→入库（完成）");
                System.out.println("  - 入库定位日志ID: " + packLocationLog.getLogId());
                System.out.println("  - 自动完成: 是");
                System.out.println("  - 操作员: " + operatorName);

                if ("2".equals(container.getPackingStatus())) {
                    // 满箱移库时记录物料信息
                    System.out.println("  - 物料编码: " + container.getCurrentMaterialCode());
                    System.out.println("  - 物料名称: " + container.getCurrentMaterialName());
                    System.out.println("  - 物料数量: " + container.getCurrentMaterialQuantity());
                    System.out.println("  - 内衬方案: " + container.getCurrentLiningScheme());
                }
            } else {
                System.out.println("更新移库记录（入库阶段）失败 - 可能没有找到对应的出库记录");
            }

            System.out.println("器具入库操作完成");

        } catch (Exception e) {
            logger.error("器具入库操作失败: {}", e.getMessage(), e);
            System.out.println("器具入库操作失败: " + e.getMessage());
        }
    }

    /**
     * 判断点是否在仓库围栏内
     * 使用射线法（Ray Casting Algorithm）判断点是否在多边形内
     *
     * @param pointLat 待判断点的纬度
     * @param pointLng 待判断点的经度
     * @param fenceList 围栏点列表
     * @return 是否在围栏内
     */
    private boolean isPointInsideWarehouseFence(BigDecimal pointLat, BigDecimal pointLng, List<PackWarehouseFence> fenceList)
    {
        if (fenceList == null || fenceList.isEmpty()) {
            return false;
        }

        // 按围栏类型和点序号排序，构建多边形
        // 目前只支持类型1（仓库边界）的围栏判断
        List<PackWarehouseFence> boundaryFences = fenceList.stream()
            .filter(fence -> "1".equals(fence.getFenceType()))
            .sorted((f1, f2) -> f1.getPointOrder().compareTo(f2.getPointOrder()))
            .collect(java.util.stream.Collectors.toList());

        if (boundaryFences.size() < 3) {
            // 少于3个点无法构成多边形
            return false;
        }

        // 使用射线法判断点是否在多边形内
        double x = pointLng.doubleValue();
        double y = pointLat.doubleValue();

        int intersectionCount = 0;
        int n = boundaryFences.size();

        for (int i = 0; i < n; i++) {
            int j = (i + 1) % n; // 下一个点的索引，最后一个点连接到第一个点

            double xi = boundaryFences.get(i).getLongitude().doubleValue();
            double yi = boundaryFences.get(i).getLatitude().doubleValue();
            double xj = boundaryFences.get(j).getLongitude().doubleValue();
            double yj = boundaryFences.get(j).getLatitude().doubleValue();

            // 检查射线是否与边相交
            if (((yi > y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi)) {
                intersectionCount++;
            }
        }

        // 奇数次相交表示点在多边形内
        return intersectionCount % 2 == 1;
    }

    /**
     * 分析从出发地到目的地的城市运输情况
     *
     * @param departureCity 出发地城市名
     * @param destinationCity 目的地城市名
     * @return 城市运输分析结果
     */
    @Override
    public CityTransitAnalysisDto analyzeCityTransit(String departureCity, String destinationCity) {
        logger.info("开始分析城市运输情况: {} -> {}", departureCity, destinationCity);

        // 1. 查询从出发地到目的地的运单
        WayBill queryParam = new WayBill();
        queryParam.setSenderCity(departureCity);
        queryParam.setReceiverCity(destinationCity);
        List<WayBill> wayBills = wayBillService.selectWayBillList(queryParam);

        if (wayBills.isEmpty()) {
            logger.warn("未找到从 {} 到 {} 的运单记录", departureCity, destinationCity);
            return createEmptyResult(departureCity, destinationCity);
        }

        logger.info("找到 {} 条从 {} 到 {} 的运单记录", wayBills.size(), departureCity, destinationCity);

        // 2. 提取运单ID列表
        List<String> wayBillIds = wayBills.stream()
            .map(WayBill::getId)
            .collect(Collectors.toList());

        // 3. 查询这些运单的定位日志
        List<PackLocationLog> locationLogs = packLocationLogMapper.selectLocationLogsByWayBillIds(wayBillIds);

        if (locationLogs.isEmpty()) {
            logger.warn("未找到运单的定位日志记录");
            return createEmptyResult(departureCity, destinationCity);
        }

        logger.info("找到 {} 条定位日志记录", locationLogs.size());

        // 4. 按运单ID分组并计算每个运单的运输时间和经过城市
        Map<String, List<PackLocationLog>> logsByWayBill = locationLogs.stream()
            .filter(log -> log.getContainerId() != null && log.getCity() != null)
            .collect(Collectors.groupingBy(log -> log.getContainerId().toString()));

        logger.info("按运单分组后的数据: 共 {} 个运单分组", logsByWayBill.size());
        for (Map.Entry<String, List<PackLocationLog>> entry : logsByWayBill.entrySet()) {
            logger.info("运单ID: {}, 定位记录数: {}", entry.getKey(), entry.getValue().size());
        }

        // 5. 分析每个运单的运输轨迹
        List<TransitRecord> transitRecords = new ArrayList<>();
        for (Map.Entry<String, List<PackLocationLog>> entry : logsByWayBill.entrySet()) {
            logger.info("开始分析运单 {} 的运输轨迹", entry.getKey());
            TransitRecord record = analyzeWayBillTransit(entry.getKey(), entry.getValue(), departureCity, destinationCity);
            if (record != null) {
                logger.info("运单 {} 分析成功，添加到结果集", entry.getKey());
                transitRecords.add(record);
            } else {
                logger.warn("运单 {} 分析失败，跳过", entry.getKey());
            }
        }

        if (transitRecords.isEmpty()) {
            logger.warn("没有有效的运输记录可供分析");
            return createEmptyResult(departureCity, destinationCity);
        }

        // 6. 汇总分析结果
        return generateAnalysisResult(departureCity, destinationCity, transitRecords);
    }

    /**
     * 分析单个运单的运输轨迹
     */
    private TransitRecord analyzeWayBillTransit(String wayBillId, List<PackLocationLog> logs,
                                               String departureCity, String destinationCity) {
        // 按时间正序排序（最早到最晚）
        logs.sort((a, b) -> a.getUploadTime().compareTo(b.getUploadTime()));

        if (logs.isEmpty()) {
            logger.debug("运单 {} 没有定位记录", wayBillId);
            return null;
        }

        // 过滤出有城市信息的记录
        List<PackLocationLog> validLogs = logs.stream()
            .filter(log -> log.getCity() != null && !log.getCity().trim().isEmpty())
            .collect(java.util.stream.Collectors.toList());

        logger.info("运单 {} 原始记录数: {}, 有效城市记录数: {}", wayBillId, logs.size(), validLogs.size());

        if (validLogs.isEmpty()) {
            logger.warn("运单 {} 没有有效的城市信息记录", wayBillId);
            return null;
        }

        // 打印排序前的记录
        logger.info("运单 {} 排序前的记录:", wayBillId);
        for (int i = 0; i < validLogs.size(); i++) {
            PackLocationLog log = validLogs.get(i);
            logger.info("  原始记录{}: 城市={}, 时间={}, 毫秒值={}",
                i+1, log.getCity(), log.getUploadTime(),
                log.getUploadTime() != null ? log.getUploadTime().getTime() : "null");
        }

        // 使用Collections.sort进行时间排序
        java.util.Collections.sort(validLogs, new java.util.Comparator<PackLocationLog>() {
            @Override
            public int compare(PackLocationLog o1, PackLocationLog o2) {
                if (o1.getUploadTime() == null && o2.getUploadTime() == null) {
                    return 0;
                }
                if (o1.getUploadTime() == null) {
                    return 1; // null值排在后面
                }
                if (o2.getUploadTime() == null) {
                    return -1;
                }
                // 直接比较毫秒时间戳
                long time1 = o1.getUploadTime().getTime();
                long time2 = o2.getUploadTime().getTime();
                return Long.compare(time1, time2);
            }
        });

        // 打印排序后的记录
        logger.info("运单 {} 排序后的记录:", wayBillId);
        for (int i = 0; i < validLogs.size(); i++) {
            PackLocationLog log = validLogs.get(i);
            logger.info("  排序记录{}: 城市={}, 时间={}, 毫秒值={}",
                i+1, log.getCity(), log.getUploadTime(),
                log.getUploadTime() != null ? log.getUploadTime().getTime() : "null");
        }

        // 根据查询条件查找对应的城市记录
        PackLocationLog departureLog = null;
        PackLocationLog destinationLog = null;

        // 查找出发城市的记录（第一次出现）
        for (PackLocationLog log : validLogs) {
            if (departureCity.equals(log.getCity().trim())) {
                departureLog = log;
                break;
            }
        }

        // 查找目的城市的记录（最后一次出现）
        for (int i = validLogs.size() - 1; i >= 0; i--) {
            PackLocationLog log = validLogs.get(i);
            if (destinationCity.equals(log.getCity().trim())) {
                destinationLog = log;
                break;
            }
        }

        // 检查是否找到了出发城市和目的城市的记录
        if (departureLog == null) {
            logger.warn("运单 {} 中未找到出发城市 {} 的记录", wayBillId, departureCity);
            return null;
        }

        if (destinationLog == null) {
            logger.warn("运单 {} 中未找到目的城市 {} 的记录", wayBillId, destinationCity);
            return null;
        }

        logger.info("运单 {} 找到记录: 出发城市 {} (时间: {}), 目的城市 {} (时间: {})",
            wayBillId, departureCity, departureLog.getUploadTime(), destinationCity, destinationLog.getUploadTime());

        // 获取出发时间和到达时间
        Date departureTime = departureLog.getUploadTime();
        Date arrivalTime = destinationLog.getUploadTime();

        // 验证时间顺序是否合理
        if (arrivalTime.before(departureTime)) {
            logger.warn("运单 {} 时间顺序异常: 到达时间 {} 早于出发时间 {}",
                wayBillId, arrivalTime, departureTime);
            return null;
        }

        // 计算运输时间（小时）
        long timeDiffMillis = arrivalTime.getTime() - departureTime.getTime();
        BigDecimal transitTimeHours = new BigDecimal(timeDiffMillis)
            .divide(new BigDecimal(3600000), 1, RoundingMode.HALF_UP);

        logger.info("运单 {} 运输时间: {} 小时", wayBillId, transitTimeHours);

        // 如果运输时间为负数或者为0，说明数据有问题
        if (transitTimeHours.compareTo(BigDecimal.ZERO) <= 0) {
            logger.warn("运单 {} 的运输时间计算异常: {} 小时", wayBillId, transitTimeHours);
            return null;
        }

        // 统计经过的所有城市（按顺序，去重）
        List<String> passedCities = new ArrayList<>();
        for (PackLocationLog log : validLogs) {
            String city = log.getCity().trim();
            if (!passedCities.contains(city)) {
                passedCities.add(city);
            }
        }

        logger.debug("运单 {} 运输轨迹: {} -> {}，耗时 {} 小时，经过城市: {}",
            wayBillId, departureCity, destinationCity, transitTimeHours, passedCities);

        return new TransitRecord(wayBillId, passedCities, transitTimeHours, validLogs);
    }

    /**
     * 生成分析结果
     */
    private CityTransitAnalysisDto generateAnalysisResult(String departureCity, String destinationCity,
                                                         List<TransitRecord> transitRecords) {
        CityTransitAnalysisDto result = new CityTransitAnalysisDto();
        result.setDepartureCity(departureCity);
        result.setDestinationCity(destinationCity);
        result.setWayBillCount(transitRecords.size());

        // 计算总平均运输时间
        BigDecimal totalTime = transitRecords.stream()
            .map(TransitRecord::getTransitTime)
            .reduce(BigDecimal.ZERO, BigDecimal::add);

        BigDecimal averageTime = totalTime.divide(new BigDecimal(transitRecords.size()), 1, RoundingMode.HALF_UP);
        result.setTotalTransitTime(averageTime);

        // 统计经过的城市信息，保持顺序
        Map<String, CityOrderInfo> cityOrderMap = new HashMap<>();

        for (TransitRecord record : transitRecords) {
            List<String> passedCities = record.getPassedCities();
            for (int i = 0; i < passedCities.size(); i++) {
                String city = passedCities.get(i);

                // 计算从起始点到该城市的总运输时间
                BigDecimal transitTime = calculateTimeFromStartToCity(city, i, record);

                // 获取或创建城市顺序信息
                CityOrderInfo orderInfo = cityOrderMap.computeIfAbsent(city, k -> new CityOrderInfo(city));

                // 记录该城市在当前运单中的顺序位置（第几个经过的城市）
                orderInfo.addOrderPosition(i + 1); // 从1开始计数
                orderInfo.addTransitTime(transitTime);
            }
        }

        // 转换为结果格式，按照平均顺序排序
        List<CityTransitAnalysisDto.CityPassInfo> passedCities = new ArrayList<>();

        // 按平均顺序位置排序城市
        List<CityOrderInfo> sortedCities = new ArrayList<>(cityOrderMap.values());
        sortedCities.sort((a, b) -> Double.compare(a.getAverageOrder(), b.getAverageOrder()));

        for (int i = 0; i < sortedCities.size(); i++) {
            CityOrderInfo orderInfo = sortedCities.get(i);
            String cityName = orderInfo.getCityName();

            // 计算平均运输时间
            BigDecimal avgTransitTime = orderInfo.getAverageTransitTime();

            // 计算运输时间标准差（最大耗时偏差）
            BigDecimal maxDeviation = orderInfo.getTransitTimeStandardDeviation();

            logger.debug("城市 {} 统计信息：平均耗时={} 小时, 最大耗时偏差(2σ)={} 小时, 样本数={}",
                cityName, avgTransitTime, maxDeviation, orderInfo.transitTimes.size());

            CityTransitAnalysisDto.CityPassInfo cityInfo = new CityTransitAnalysisDto.CityPassInfo(cityName, "");
            cityInfo.setPassOrder(i + 1); // 设置最终的顺序编号
            cityInfo.setAverageStayTime(avgTransitTime); // 存储从起始点到该城市的平均运输时间
            cityInfo.setMaxDeviation(maxDeviation); // 设置最大耗时偏差（标准差）

            passedCities.add(cityInfo);
        }

        result.setPassedCities(passedCities);
        return result;
    }



    /**
     * 计算从起始点到指定城市的总运输时间
     */
    private BigDecimal calculateTimeFromStartToCity(String city, int cityIndex, TransitRecord record) {
        List<String> cities = record.getPassedCities();
        if (cityIndex == 0) {
            return BigDecimal.ZERO; // 起始城市的时间为0
        }

        // 过滤出有城市信息的记录，并按时间排序
        List<PackLocationLog> validLogs = record.getLogs().stream()
            .filter(log -> log.getCity() != null && !log.getCity().trim().isEmpty())
            .sorted((a, b) -> {
                if (a.getUploadTime() == null && b.getUploadTime() == null) return 0;
                if (a.getUploadTime() == null) return 1;
                if (b.getUploadTime() == null) return -1;
                return Long.compare(a.getUploadTime().getTime(), b.getUploadTime().getTime());
            })
            .collect(java.util.stream.Collectors.toList());

        if (validLogs.size() < 2) {
            return BigDecimal.ZERO;
        }

        // 找到起始城市的第一次记录时间
        String startCity = cities.get(0);
        String targetCity = cities.get(cityIndex);

        Date startTime = null;
        Date targetCityFirstTime = null;

        // 找到起始城市的第一次记录时间（最早的记录）
        for (PackLocationLog log : validLogs) {
            if (startCity.equals(log.getCity().trim())) {
                startTime = log.getUploadTime();
                break;
            }
        }

        // 找到目标城市的第一次记录时间（第一次进入该城市的时间）
        for (PackLocationLog log : validLogs) {
            if (targetCity.equals(log.getCity().trim())) {
                targetCityFirstTime = log.getUploadTime();
                break;
            }
        }

        // 计算从起始点到目标城市的总运输时间
        if (startTime != null && targetCityFirstTime != null && targetCityFirstTime.after(startTime)) {
            long timeDiff = targetCityFirstTime.getTime() - startTime.getTime();
            return new BigDecimal(timeDiff).divide(new BigDecimal(3600000), 1, RoundingMode.HALF_UP);
        }

        return BigDecimal.ZERO;
    }

    /**
     * 创建空的分析结果
     */
    private CityTransitAnalysisDto createEmptyResult(String departureCity, String destinationCity) {
        CityTransitAnalysisDto result = new CityTransitAnalysisDto();
        result.setDepartureCity(departureCity);
        result.setDestinationCity(destinationCity);
        result.setWayBillCount(0);
        result.setTotalTransitTime(BigDecimal.ZERO);
        result.setPassedCities(new ArrayList<>());
        return result;
    }

    /**
     * 运输记录内部类
     */
    @Getter
    private static class TransitRecord {
        // Getters
        private String wayBillId;
        private List<String> passedCities;
        private BigDecimal transitTime;
        private List<PackLocationLog> logs;

        public TransitRecord(String wayBillId, List<String> passedCities,
                           BigDecimal transitTime, List<PackLocationLog> logs) {
            this.wayBillId = wayBillId;
            this.passedCities = passedCities;
            this.transitTime = transitTime;
            this.logs = logs;
        }

    }


    /**
     * 城市顺序信息内部类
     */
    private static class CityOrderInfo {
        @Getter
        private String cityName;
        private List<Integer> orderPositions; // 在各个运单中的顺序位置
        private List<BigDecimal> transitTimes; // 运输时间列表

        public CityOrderInfo(String cityName) {
            this.cityName = cityName;
            this.orderPositions = new ArrayList<>();
            this.transitTimes = new ArrayList<>();
        }

        public void addOrderPosition(int position) {
            this.orderPositions.add(position);
        }

        public void addTransitTime(BigDecimal transitTime) {
            this.transitTimes.add(transitTime);
        }

        // 计算平均顺序位置
        public double getAverageOrder() {
            if (orderPositions.isEmpty()) return 0.0;
            return orderPositions.stream().mapToInt(Integer::intValue).average().orElse(0.0);
        }

        // 计算平均运输时间
        public BigDecimal getAverageTransitTime() {
            if (transitTimes.isEmpty()) return BigDecimal.ZERO;

            BigDecimal total = transitTimes.stream()
                .reduce(BigDecimal.ZERO, BigDecimal::add);
            return total.divide(new BigDecimal(transitTimes.size()), 1, RoundingMode.HALF_UP);
        }

        /**
         * 计算运输时间的最大耗时偏差（两个标准差）
         * 使用2σ覆盖约95%的数据范围，适合风险评估和时间预警
         * @return 最大耗时偏差值（2σ）
         */
        public BigDecimal getTransitTimeStandardDeviation() {
            if (transitTimes.size() <= 1) {
                return BigDecimal.ZERO; // 样本数量不足，返回0
            }

            // 1. 计算平均值
            BigDecimal mean = getAverageTransitTime();

            // 2. 计算方差 = Σ(xi - μ)² / (n-1)  使用样本标准差
            BigDecimal variance = BigDecimal.ZERO;
            for (BigDecimal time : transitTimes) {
                BigDecimal diff = time.subtract(mean);
                BigDecimal squared = diff.multiply(diff);
                variance = variance.add(squared);
            }

            // 除以 (n-1) 得到样本方差
            variance = variance.divide(new BigDecimal(transitTimes.size() - 1), 4, RoundingMode.HALF_UP);

            // 3. 计算标准差 = √方差
            double varianceDouble = variance.doubleValue();
            double standardDeviationDouble = Math.sqrt(varianceDouble);

            // 使用两个标准差作为最大耗时偏差（覆盖约95%的数据范围）
            double twoSigma = standardDeviationDouble * 2;

            return new BigDecimal(twoSigma).setScale(2, RoundingMode.HALF_UP);
        }
    }
}
